#include <limits>
#include <math.h>
#include <string.h>
#include <string>
#include <fstream>
#include <util/box/box.hpp>
#include <problems/pareto/multiopt/multidata.hpp>
#include <problems/pareto/multicov/multicov.hpp>
#include <problems/pareto/multicov/lipbounder.hpp>
#include <problems/pareto/multicov/subbag.hpp>
#include <problems/pareto/multiopt/mpsimpbag.hpp>
#include <problems/pareto/multicov/multistate.hpp>
#include <problems/pareto/multicov/compbounder.hpp>
#include <common/robot.hpp>
#include <common/robutils.hpp>
#include <common/robibounder.hpp>
#include <common/anglebounder.hpp>
#include <common/anglecons.hpp>

typedef double T;


#define MAXLEN 8192
#define MAXD 3

T signs[MAXD];

int dim;

void setSign(char* side) {
    if (!strcmp(side, "ne")) {
        signs[0] = -1;
        signs[1] = -1;
    } else if (!strcmp(side, "nw")) {
        signs[0] = 1;
        signs[1] = -1;
    } else if (!strcmp(side, "se")) {
        signs[0] = -1;
        signs[1] = 1;
    } else if (!strcmp(side, "sw")) {
        signs[0] = 1;
        signs[1] = 1;
    }
}

void applySign(MPSimpBag<T>& bag) {
    int n = bag.size();
    for (int i = 0; i < n; i++) {
        bag[i].mCrit.mX[0] *= signs[0];
        bag[i].mCrit.mX[1] *= signs[1];
    }

}

void input(char* fname, std::string& s) {
    std::ifstream is;
    is.open(fname);
    char buf[MAXLEN];
    is.read(buf, MAXLEN);
    s.append(buf, is.gcount());
    is.close();
}

int main(int argc, char* argv[]) {
    if (argc != 3) {
        BNB_ERROR_REPORT("usage: srobot.exe input ne|nw|se|sw");
    }
    std::string s;
    input(argv[1], s);
    setSign(argv[2]);
    std::vector< RobotObjective<T> > rov;
    RobotData<T> rd;
    RobUtils<T>::fromJson(s, rd);
    std::string s1;
    RobUtils<T>::toString(rd, s1);
    printf("robot data: %s\n", s1.c_str());
    BNB_ASSERT(rd.mDim == 2);
    RobotObjective<T> robx(RobotObjective<T>::Coords::X, rd.mNSec, rd.mSections);
    RobotObjective<T> roby(RobotObjective<T>::Coords::Y, rd.mNSec, rd.mSections);
    rov.push_back(robx);
    rov.push_back(roby);
    MultiData<double> md(*(rd.mBox));
    for (int i = 0; i < rd.mDim; i++) {
        rov[i].setSign(signs[i]);
    }
    for (int i = 0; i < rov.size(); i++) {
        md.mCriteria.push_back(&(rov[i]));
    }
    if (rd.mAngles != NULL) {
        AngleCons<double> ac1(rd.mNSec, rd.mAngles[0], -1);
        AngleCons<double> ac2(rd.mNSec, rd.mAngles[1], 1);
        md.mConstraints.push_back(&ac1);
        md.mConstraints.push_back(&ac2);
    }
    LipBounder<double> lb(&md);
    RobIntervalBounder<double> rb(rd.mNSec, rd.mSections, signs);
    CompositeBounder<double> cb;
    cb.push(&rb);
    if (rd.mAngles != NULL) {
        AngleBounder<double> ab(2 * (rd.mDim - 1), rd.mAngles);
        cb.push(&ab);
    }
    MultiCov<double> ml(md);
    //    ml.addBounder(&lb);
    ml.addBounder(&cb);
//    ml.setBoundingMethod(1);
    MultiState< MPSimpBag < double >, SubBag<double> > ms;

    MultiSub<double> sub(md.mBox.mDim, 1);
    sub.mBox = md.mBox;
    ms.mSubBag.put(sub);
    ml.setPrecision(rd.mEps, rd.mDelta);
    long long nit = std::numeric_limits<long long>::max();

    ml.solve(ms, nit);

    applySign(ms.mPointBag);
    printf("%s", ms.mPointBag.toString().c_str());
    printf("\n%lld iterations performed\n", nit);
}
